package com.mccken.springframework.context.support;

import java.util.Map;

import com.mccken.springframework.beans.BeansException;
import com.mccken.springframework.beans.factory.ConfigurableListableBeanFactory;
import com.mccken.springframework.beans.factory.config.BeanFactoryPostProcessor;
import com.mccken.springframework.beans.factory.config.BeanPostProcessor;
import com.mccken.springframework.context.ConfigurableApplicationContext;
import com.mccken.springframework.core.io.DefaultResourceLoader;

import cn.hutool.core.bean.BeanException;

/**
 * @program: mccken-collect
 * @description:
 * @author: mccken
 * @create: 2022-12-02 10:38
 **/
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

	@Override
	public void refresh() throws BeansException {
		
		// 1. 创建 BeanFactory ,并加载 BeanDefinition
		refreshBeanFactory();
		
		// 2. 获取 BeanFactory
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
		
		// 3. 在将 Bean 对象实例化之前，执行 BeanFactoryPostProcessor 操作
		invokeBeanFactoryPostProcessor(beanFactory);
		
		// 4. BeanPostProcessor 需要在 Bean 实例化之前就 注册完
		registerBeanPostProcessors(beanFactory);
		
		// 5. 提前实例化单例Bean对象
		beanFactory.preInstantiateSingletons();
	}

	private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
		Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
		for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
			beanFactory.addBeanPostProcessor(beanPostProcessor);
		}
	}

	private void invokeBeanFactoryPostProcessor(ConfigurableListableBeanFactory beanFactory) {
		Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
		for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
			beanFactoryPostProcessor.postProcessorBeanFactory(beanFactory);	
		}
	}

	protected abstract void refreshBeanFactory();
	protected abstract ConfigurableListableBeanFactory getBeanFactory();

	@Override
	public Object getBean(String name) throws BeansException {
		return getBeanFactory().getBean(name);
	}

	@Override
	public Object getBean(String name, Object... args) throws BeansException {
		return getBeanFactory().getBean(name, args);
	}

	@Override
	public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
		return getBeanFactory().getBean(name, requiredType);
	}

	@Override
	public <T> Map<String, T> getBeansOfType(Class<T> type) throws BeanException {
		return getBeanFactory().getBeansOfType(type);
	}

	@Override
	public String[] getBeanDefinitionNames() {
		return getBeanFactory().getBeanDefinitionNames();
	}
}
